#include <framework/framework_i.h>

static struct gatedesc_s idt[IDT_COUNT];
static struct pseudodesc_s idt_pd = {
     sizeof(idt) - 1, (unsigned long)idt
};

static volatile ex_handler_f	ex_handlers[LAPIC_COUNT][ISR_COUNT];
static volatile irq_handler_f	irq_handlers[LAPIC_COUNT][IRQ_COUNT];
volatile sys_handler_f          sys_handlers[LAPIC_COUNT];

void
intr_irq_eoi(int irq)
{
	 if (ekf_sysconf.use_pic)
		  pic_send_eoi(irq);
	 if (ekf_sysconf.use_lapic)
		  lapic_send_eoi(irq);
}

int
intr_init(void)
{
	 uint32_t i;
	 /* Setting up the idt */
     for (i = 0; i != IDT_COUNT; ++i)
		  SET_GATE(idt[i], i < 32 ? false : true, GD_KERN_TEXT, isr[i], 3);
	 /* All IRQ interruption would disable further interruption */
     for (i = 0; i != IRQ_COUNT; ++i)
		  SET_GATE(idt[IRQ_OFFSET + i], false, GD_KERN_TEXT, irq[i], 3);

	 /* USER-MODE-KERNEL Calls */
	 SET_GATE(idt[INT_SYSCALL], true, GD_KERN_TEXT, syscall, 3);
     /* Inter processor interrupt */
	 SET_GATE(idt[INT_IPI], false, GD_KERN_TEXT, ipi, 3);
	 
     /* flush the idt */
     asm volatile("lidt (%0)" : : "r"(&idt_pd));

     memset((void *)ex_handlers, 0, sizeof(ex_handlers));
     memset((void *)irq_handlers, 0, sizeof(irq_handlers));
	 
	 pic_timer_init(0);

	 if (ekf_sysconf.use_ioapic)
	 {
		  /* TODO? */
	 }

     return 0;
}

int
intr_init_ap(void)
{
	 /* flush the idt */
	 asm volatile("lidt (%0)" : : "r"(&idt_pd));
}

void
kexception(struct trapframe_s tf)
{
	 int cpu = lcpu_id;
     if (ex_handlers[cpu][tf.num] != NULL) {
		  ex_handlers[cpu][tf.num](&tf);
	 }
     else if (tf.num == 14)
	 {
		  cprintf(" --- [%d]Page fault addr = %08x, error num %04x at vaddr 0x%08x\n",
				  cpu, rcr2(), tf.err, tf.hw.eip);
		  monitor(&tf);

	 }
	 else
     {
		  cprintf(" --- [%d]Got kernel expection %d, error num %04x at vaddr 0x%08x\n",
				  cpu, tf.num, tf.err, tf.hw.eip);
		  monitor(&tf);
	 }
}

void
intr_ex_handler_set(uint32_t n, ex_handler_f h)
{
     ex_handlers[lcpu_id][n] = h;
}

void
kirq(struct trapframe_s tf)
{	 
	 int cpu = lcpu_id;
	 if (irq_handlers[cpu][tf.num] != NULL)
		  irq_handlers[cpu][tf.num](&tf);
	 else
	 {
		  cprintf(" [%d]Got unknown irq request %d\n", cpu, tf.num);
		  intr_irq_eoi(tf.num);
	 }
}

void
intr_irq_handler_set(uint32_t irq, irq_handler_f h)
{
     irq_handlers[lcpu_id][irq] = h;
}

void
intr_sys_handler_set(sys_handler_f h)
{
	 sys_handlers[lcpu_id] = h;
}

int
intr_timer_set(uint32_t freq)
{
	 if (ekf_sysconf.use_pic)
	 {
	 	  pic_timer_init(freq);
	 	  pic_enable(0);

		  return 0;
	 }
	 else if (ekf_sysconf.use_lapic)
	 {
		  lapic_set_timer(freq);
		  return 0;
	 }
	 else return -1;
}

int
intr_trap_from_user(struct trapframe_s *tf)
{
	 return tf->hw.cs != GD_KERN_TEXT;
}

void
intr_irq_line_enable(int irq)
{
	 if (ekf_sysconf.use_pic)
	 {
		  pic_enable(irq);
	 }
	 if (ekf_sysconf.use_ioapic)
	 {
		  void *ioapic;
		  BEGIN_TPAGE(0xFEC00000, ioapic);
		  ioapic_enable(ioapic, irq, 0);
		  END_TPAGE;
	 }
}

void
intr_irq_line_disable(int irq)
{
	 /* XXX */
}
